home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1996 March / EnigmA AMIGA RUN 05 (1996)(G.R. Edizioni)(IT)[!][issue 1996-03][Skylink CD IV].iso / earcd / gnu / gnpltsrc.lha / help.c < prev    next >
C/C++ Source or Header  |  1996-01-22  |  20KB  |  736 lines

  1. #ifndef lint
  2. static char *RCSid = "$Id: help.c,v 1.20 1995/12/02 22:04:33 drd Exp $";
  3. #endif
  4.  
  5.  
  6. /* GNUPLOT - help.c */
  7. /*
  8.  * Copyright (C) 1986 - 1993   Thomas Williams, Colin Kelley
  9.  *
  10.  * Permission to use, copy, and distribute this software and its
  11.  * documentation for any purpose with or without fee is hereby granted, 
  12.  * provided that the above copyright notice appear in all copies and 
  13.  * that both that copyright notice and this permission notice appear 
  14.  * in supporting documentation.
  15.  *
  16.  * Permission to modify the software is granted, but not the right to
  17.  * distribute the modified code.  Modifications are to be distributed 
  18.  * as patches to released version.
  19.  *  
  20.  * This software is provided "as is" without express or implied warranty.
  21.  * 
  22.  *
  23.  * AUTHORS
  24.  * 
  25.  *   Original Software:
  26.  *     Thomas Williams,  Colin Kelley.
  27.  * 
  28.  *   Gnuplot 2.0 additions:
  29.  *       Russell Lang, Dave Kotz, John Campbell.
  30.  *
  31.  *   Gnuplot 3.0 additions:
  32.  *       Gershon Elber and many others.
  33.  * 
  34.  * There is a mailing list for gnuplot users. Note, however, that the
  35.  * newsgroup 
  36.  *     comp.graphics.gnuplot 
  37.  * is identical to the mailing list (they
  38.  * both carry the same set of messages). We prefer that you read the
  39.  * messages through that newsgroup, to subscribing to the mailing list.
  40.  * (If you can read that newsgroup, and are already on the mailing list,
  41.  * please send a message info-gnuplot-request@dartmouth.edu, asking to be
  42.  * removed from the mailing list.)
  43.  *
  44.  * The address for mailing to list members is
  45.  *       info-gnuplot@dartmouth.edu
  46.  * and for mailing administrative requests is 
  47.  *       info-gnuplot-request@dartmouth.edu
  48.  * The mailing list for bug reports is 
  49.  *       bug-gnuplot@dartmouth.edu
  50.  * The list of those interested in beta-test versions is
  51.  *       info-gnuplot-beta@dartmouth.edu
  52.  */
  53.  
  54. #include "plot.h"
  55.  
  56. #define    SAME    0    /* for strcmp() */
  57.  
  58. #include "help.h"    /* values passed back */
  59.  
  60. /* since there are only two external references, we won't include protos.h */
  61.  
  62. void int_error __P((char str[], int t_num));
  63. int instring __P((char *str, char c));
  64.  
  65. #if defined(__EMX__) || defined(DJGPP) || defined(DOS386)
  66. #ifdef MSDOS
  67. #undef MSDOS    /* we have plenty of memory under __EMX__ or DJGPP */
  68. #endif
  69. #ifdef unix
  70. #undef unix    /* we are not unix */
  71. #endif
  72. #endif
  73.  
  74. #ifdef OS2
  75.   /* GCC defines unix, but no PAGER, so... */
  76. #ifdef unix
  77. #undef unix
  78. #endif
  79. #endif  /* OS2 */
  80.  
  81. /* help -- help subsystem that understands defined keywords
  82. **
  83. ** Looks for the desired keyword in the help file at runtime, so you
  84. ** can give extra help or supply local customizations by merely editing
  85. ** the help file.
  86. **
  87. ** The original (single-file) idea and algorithm is by John D. Johnson,
  88. ** Hewlett-Packard Company.  Thanx and a tip of the Hatlo hat!
  89. **
  90. ** Much extension by David Kotz for use in gnutex, and then in gnuplot.
  91. ** Added output paging support, both unix and builtin. Rewrote completely
  92. ** to read helpfile into memory, avoiding reread of help file. 12/89.
  93. **
  94. ** Modified by Russell Lang to avoid reading completely into memory
  95. ** if MSDOS defined.  This uses much less memory.  6/91
  96. **
  97. ** The help file looks like this (the question marks are really in column 1):
  98. **
  99. **     ?topic
  100. **     This line is printed when the user wants help on "topic".
  101. **     ?keyword
  102. **     ?Keyword
  103. **     ?KEYWORD
  104. **     These lines will be printed on the screen if the user wanted
  105. **     help on "keyword", "Keyword", or "KEYWORD".  No casefolding is
  106. **    done on the keywords.
  107. **     ?subject
  108. **     ?alias
  109. **     This line is printed for help on "subject" and "alias".
  110. **     ?
  111. **    ??
  112. **     Since there is a null keyword for this line, this section
  113. **     is printed when the user wants general help (when a help
  114. **     keyword isn't given).  A command summary is usually here.
  115. **    Notice that the null keyword is equivalent to a "?" keyword
  116. **    here, because of the '?' and '??' topic lines above.
  117. **   If multiple keywords are given, the first is considered the 
  118. **   'primary' keyword. This affects a listing of available topics.
  119. **     ?last-subject
  120. **     Note that help sections are terminated by the start of the next
  121. **     '?' entry or by EOF.  So you can't have a leading '?' on a line
  122. **     of any help section.  You can re-define the magic character to
  123. **    recognize in column 1, though, if '?' is too useful.  (Try ^A.)
  124. */
  125.  
  126. #define    KEYFLAG    '?'    /* leading char in help file topic lines */
  127.  
  128. /*
  129. ** Calling sequence:
  130. **    int result;        # 0 == success
  131. **    char *keyword;        # topic to give help on
  132. **    char *pathname;        # path of help file
  133. **      int subtopics;        # set to TRUE if only subtopics to be listed
  134. **                # returns TRUE if subtopics were found
  135. **    result = help(keyword, pathname, &subtopics);
  136. ** Sample:
  137. **    cmd = "search\n";
  138. **    helpfile = "/usr/local/lib/program/program.help";
  139. **    subtopics = FALSE;
  140. **    if (help(cmd, helpfile, &subtopics) != H_FOUND)
  141. **        printf("Sorry, no help for %s", cmd);
  142. **
  143. **
  144. ** Speed this up by replacing the stdio calls with open/close/read/write.
  145. */
  146. #ifdef    WDLEN
  147. #  define    PATHSIZE    WDLEN
  148. #else
  149. #  define    PATHSIZE    BUFSIZ
  150. #endif
  151.  
  152. typedef int boolean;
  153. #ifndef TRUE
  154. #define TRUE (1)
  155. #define FALSE (0)
  156. #endif
  157.  
  158. typedef struct line_s LINEBUF;
  159. struct line_s {
  160.     char *line;            /* the text of this line */
  161.     LINEBUF *next;            /* the next line */
  162. };
  163.  
  164. typedef struct linkey_s LINKEY;
  165. struct linkey_s {
  166.     char *key;                /* the name of this key */
  167.     long pos;                /* ftell position */
  168.     LINEBUF *text;            /* the text for this key */
  169.     boolean primary;        /* TRUE -> is a primary name for a text block */
  170.     LINKEY *next;            /* the next key in linked list */
  171. };
  172.  
  173. typedef struct key_s KEY;
  174. struct key_s {
  175.     char *key;                /* the name of this key */
  176.     long pos;                /* ftell position */
  177.     LINEBUF *text;            /* the text for this key */
  178.     boolean primary;        /* TRUE -> is a primary name for a text block */
  179. };
  180. static LINKEY *keylist = NULL;    /* linked list of keys */
  181. static KEY *keys = NULL;        /* array of keys */
  182. static int keycount = 0;        /* number of keys */
  183. static FILE *helpfp = NULL;
  184.  
  185. static int LoadHelp __P((char *path));
  186. static void sortkeys __P((void));
  187. static int keycomp __P((struct key_s *a, struct key_s *b));
  188. static LINEBUF *storeline __P((char *text));
  189. static LINKEY *storekey __P((char *key));
  190. static KEY *FindHelp __P((char *keyword));
  191. static boolean Ambiguous __P((struct key_s *key, int len));
  192.  
  193. /* Help output */
  194. static void PrintHelp __P((struct key_s *key, int *subtopics));
  195. static void ShowSubtopics __P((struct key_s *key, int *subtopics));
  196. static void StartOutput __P((void));
  197. static void OutLine __P((char *line));
  198. static void EndOutput __P((void));
  199. static FILE *outfile;        /* for unix pager, if any */
  200. static int pagelines;        /* count for builtin pager */
  201. #define SCREENSIZE 24        /* lines on screen (most have at least 24) */
  202.  
  203. /* help:
  204.  * print a help message 
  205.  * also print available subtopics, if subtopics is TRUE
  206.  */
  207. int help(keyword, path, subtopics)
  208.     char *keyword;        /* on this topic */
  209.     char *path;            /* from this file */
  210.     boolean *subtopics;    /* (in) - subtopics only? */
  211.                         /* (out) - are there subtopics? */
  212. {
  213.     static char oldpath[PATHSIZE] = "";    /* previous help file */
  214.     int status;            /* result of LoadHelp */
  215.     KEY *key;            /* key that matches keyword */
  216.  
  217.     /*
  218.     ** Load the help file if necessary (say, first time we enter this routine,
  219.     ** or if the help file changes from the last time we were called).
  220.     ** Also may occur if in-memory copy was freed.
  221.     ** Calling routine may access errno to determine cause of H_ERROR.
  222.     */
  223.     errno = 0;
  224.     if (strncmp(oldpath, path, PATHSIZE) != SAME)
  225.      FreeHelp();
  226.     if (keys == NULL) {
  227.        status = LoadHelp(path);
  228.        if (status == H_ERROR)
  229.         return(status);
  230.  
  231.        /* save the new path in oldpath */
  232.        if (strlen(path) < PATHSIZE)
  233.         (void) strcpy(oldpath, path);
  234.        else {                /* not enough room in oldpath, sigh */
  235.           (void) strncpy(oldpath, path, PATHSIZE - 1);
  236.           oldpath[PATHSIZE - 1] = '\0';
  237.        }
  238.     }
  239.  
  240.     /* look for the keyword in the help file */
  241.     key = FindHelp(keyword);
  242.     if (key != NULL) {
  243.        /* found the keyword: print help and return */
  244.        PrintHelp(key, subtopics);
  245.        status = H_FOUND;
  246.     } else {
  247.        status = H_NOTFOUND;
  248.     }
  249.  
  250.     return(status);
  251. }
  252.  
  253. /* we only read the file once, into memory
  254.  * except for MSDOS when we don't read all the file -
  255.  * just the keys and location of the text
  256.  */
  257. static int
  258. LoadHelp(path)
  259.     char *path;
  260. {
  261.     LINKEY *key;            /* this key */
  262.     long pos;                /* ftell location within help file */
  263.     char buf[BUFSIZ];        /* line from help file */
  264.     LINEBUF *head;            /* head of text list  */
  265.     LINEBUF *firsthead = NULL;
  266.     boolean primary;        /* first ? line of a set is primary */
  267.     boolean flag;
  268.  
  269.     if ((helpfp = fopen(path, "r")) == NULL) {
  270.        /* can't open help file, so error exit */
  271.        return (H_ERROR);
  272.     }
  273.  
  274.     /*
  275.     ** The help file is open.  Look in there for the keyword.
  276.     */
  277.     if (!fgets(buf, BUFSIZ - 1, helpfp) || *buf != KEYFLAG)
  278.         return (H_ERROR); /* it is probably not the .gih file */
  279.         
  280.     while (!feof(helpfp)) {
  281.        /*
  282.         ** Make an entry for each synonym keyword
  283.         */
  284.        primary = TRUE;
  285.        while (buf[0] == KEYFLAG) {
  286.           key = storekey(buf+1);    /* store this key */
  287.           key->primary = primary;
  288.           key->text = NULL;            /* fill in with real value later */
  289.           key->pos = 0;                /* fill in with real value later */
  290.           primary = FALSE;
  291.           pos = ftell(helpfp);
  292.           if (fgets(buf, BUFSIZ - 1, helpfp) == (char *)NULL)
  293.             break;
  294.        }
  295.        /*
  296.         ** Now store the text for this entry.
  297.         ** buf already contains the first line of text.
  298.         */
  299. #ifndef MSDOS
  300.        firsthead = storeline(buf);
  301.        head = firsthead;
  302. #endif
  303.        while ( (fgets(buf, BUFSIZ - 1, helpfp) != (char *)NULL)
  304.         && (buf[0] != KEYFLAG) ){
  305. #ifndef MSDOS
  306.           /* save text line */
  307.           head->next = storeline(buf);
  308.           head = head->next;
  309. #endif
  310.        }
  311.        /* make each synonym key point to the same text */
  312.        do {
  313.           key->pos = pos;
  314.           key->text = firsthead;
  315.           flag = key->primary;
  316.           key = key->next;
  317.        } while ( flag!=TRUE  &&  key!=NULL );
  318.     }
  319. #ifndef MSDOS
  320.     (void) fclose(helpfp);
  321. #endif
  322.  
  323.     /* we sort the keys so we can use binary search later */
  324.     sortkeys();
  325.     return(H_FOUND); /* ok */
  326. }
  327.  
  328. /* make a new line buffer and save this string there */
  329. static LINEBUF *
  330. storeline(text)
  331.     char *text;
  332. {
  333.     LINEBUF *new;
  334.  
  335.     new = (LINEBUF *)malloc(sizeof(LINEBUF));
  336.     if (new == NULL)
  337.      int_error("not enough memory to store help file", -1);
  338.     if (text != NULL) {
  339.        new->line = (char *) malloc((unsigned int)(strlen(text)+1));
  340.        if (new->line == NULL)
  341.         int_error("not enough memory to store help file", -1);
  342.        (void) strcpy(new->line, text);
  343.     } else
  344.      new->line = NULL;
  345.  
  346.     new->next = NULL;
  347.  
  348.     return(new);
  349. }
  350.  
  351. /* Add this keyword to the keys list, with the given text */
  352. static LINKEY *
  353. storekey(key)
  354.     char *key;
  355. {
  356.     LINKEY *new;
  357.  
  358.     key[strlen(key)-1] = '\0'; /* cut off \n  */
  359.  
  360.     new = (LINKEY *)malloc(sizeof(LINKEY));
  361.     if (new == NULL)
  362.      int_error("not enough memory to store help file", -1);
  363.     new->key = (char *) malloc((unsigned int)(strlen(key)+1));
  364.     if (new->key == NULL)
  365.      int_error("not enough memory to store help file", -1);
  366.     (void) strcpy(new->key, key);
  367.  
  368.     /* add to front of list */
  369.     new->next = keylist;
  370.     keylist = new;
  371.     keycount++;
  372.     return(new);
  373. }
  374.  
  375. /* we sort the keys so we can use binary search later */
  376. /* We have a linked list of keys and the number.
  377.  * to sort them we need an array, so we reform them into an array,
  378.  * and then throw away the list.
  379.  */
  380. static void
  381. sortkeys()
  382. {
  383.     LINKEY *p,*n;            /* pointers to linked list */
  384.     int i;                /* index into key array */
  385.     
  386.     /* allocate the array */
  387.     keys = (KEY *)malloc((unsigned int)((keycount+1) * sizeof(KEY)));
  388.     if (keys == NULL)
  389.      int_error("not enough memory to store help file", -1);
  390.     
  391.     /* copy info from list to array, freeing list */
  392.     for (p = keylist, i = 0; p != NULL; p = n, i++) {
  393.        keys[i].key = p->key;
  394.        keys[i].pos = p->pos;
  395.        keys[i].text = p->text;
  396.        keys[i].primary = p->primary;
  397.        n = p->next;
  398.        free( (char *)p );
  399.     }
  400.  
  401.     /* a null entry to terminate subtopic searches */
  402.     keys[keycount].key = NULL;
  403.     keys[keycount].pos = 0;
  404.     keys[keycount].text = NULL;
  405.  
  406.     /* sort the array */
  407.     /* note that it only moves objects of size (two pointers + long + int) */
  408.     /* it moves no strings */
  409.     qsort((char *)keys, keycount, sizeof(KEY), (sortfunc)keycomp);
  410. }
  411.  
  412. static int
  413. keycomp(a, b)
  414.     KEY *a,*b;
  415. {
  416.     return (strcmp(a->key, b->key));
  417. }
  418.  
  419. /* Free the help file from memory. */
  420. /* May be called externally if space is needed */
  421. void
  422. FreeHelp()
  423. {
  424.     int i;                /* index into keys[] */
  425.     LINEBUF *t, *next;
  426.  
  427.     if (keys == NULL)
  428.      return;
  429.  
  430.     for (i = 0; i < keycount; i++) {
  431.        free( (char *)keys[i].key );
  432.        if (keys[i].primary)   /* only try to release text once! */
  433.        for (t = keys[i].text; t != NULL; t = next) {
  434.           free( (char *)t->line );
  435.           next = t->next;
  436.           free( (char *)t );
  437.        }
  438.     }
  439.     free( (char *)keys );
  440.     keys = NULL;
  441.     keycount = 0;
  442. #ifdef MSDOS
  443.     (void) fclose(helpfp);
  444. #endif
  445. }
  446.  
  447. /* FindHelp:
  448.  *  Find the key that matches the keyword.
  449.  *  The keys[] array is sorted by key.
  450.  *  We could use a binary search, but a linear search will aid our
  451.  *  attempt to allow abbreviations. We search for the first thing that
  452.  *  matches all the text we're given. If not an exact match, then
  453.  *  it is an abbreviated match, and there must be no other abbreviated
  454.  *  matches -- for if there are, the abbreviation is ambiguous. 
  455.  *  We print the ambiguous matches in that case, and return not found.
  456.  */
  457. static KEY *                /* NULL if not found */
  458. FindHelp(keyword)
  459.     char *keyword;            /* string we look for */
  460. {
  461.     KEY *key;
  462.     int len = strlen(keyword);
  463.     int compare;
  464.  
  465.     for (key = keys, compare = 1; key->key != NULL && compare > 0; key++) {
  466.        compare = strncmp(keyword, key->key, len);
  467.        if (compare == 0)    /* we have a match! */
  468.         if (!Ambiguous(key, len)) {
  469.             /* non-ambiguous abbreviation */
  470.             (void) strcpy(keyword, key->key); /* give back the full spelling */
  471.             return(key);        /* found!! */
  472.         }
  473.     }
  474.  
  475.     /* not found, or ambiguous */
  476.     return(NULL);
  477. }
  478.  
  479. /* Ambiguous:
  480.  * Check the key for ambiguity up to the given length.
  481.  * It is ambiguous if it is not a complete string and there are other
  482.  * keys following it with the same leading substring.
  483.  */
  484. static boolean
  485. Ambiguous(key, len)
  486.     KEY *key;
  487.     int len;
  488. {
  489.     char *first;
  490.     char *prev;
  491.     boolean status = FALSE;    /* assume not ambiguous */
  492.     int compare;
  493.     int sublen;
  494.  
  495.     if (key->key[len] == '\0')
  496.      return(FALSE);
  497.     
  498.     for (prev = first = key->key, compare = 0, key++;
  499.         key->key != NULL && compare == 0; key++) {
  500.        compare = strncmp(first, key->key, len);
  501.        if (compare == 0) {
  502.           /* So this key matches the first one, up to len.
  503.            * But is it different enough from the previous one
  504.            * to bother printing it as a separate choice?
  505.            */
  506.           sublen = instring(prev+len, ' ');
  507.           if (strncmp(key->key, prev, len+sublen) != 0) {
  508.              /* yup, this is different up to the next space */
  509.              if (!status) {
  510.                 /* first one we have printed is special */
  511.                 fprintf(stderr, 
  512.                        "Ambiguous request '%.*s'; possible matches:\n",
  513.                        len, first);
  514.                 fprintf(stderr, "\t%s\n", prev);
  515.                 status = TRUE;
  516.              }
  517.              fprintf(stderr, "\t%s\n", key->key);
  518.              prev = key->key;
  519.           }
  520.        }
  521.     }
  522.     
  523.     return(status);
  524. }
  525.  
  526. /* PrintHelp:
  527.  * print the text for key
  528.  */
  529. static void
  530. PrintHelp(key, subtopics)
  531.     KEY *key;
  532.     boolean *subtopics;        /* (in) - subtopics only? */
  533.                         /* (out) - are there subtopics? */
  534. {
  535.     LINEBUF *t;
  536. #ifdef MSDOS
  537.     char buf[BUFSIZ];        /* line from help file */
  538. #endif
  539.  
  540.     StartOutput();
  541.  
  542.     if (subtopics == NULL || !*subtopics) {
  543. #ifdef MSDOS
  544.        fseek(helpfp,key->pos,0);
  545.        while ( (fgets(buf, BUFSIZ - 1, helpfp) != (char *)NULL)
  546.             && (buf[0] != KEYFLAG) ) {
  547.           OutLine(buf);
  548.        }
  549. #else
  550.        for (t = key->text; t != NULL; t = t->next)
  551.         OutLine(t->line);        /* print text line */
  552. #endif
  553.     }
  554.  
  555.     ShowSubtopics(key, subtopics);
  556.     OutLine("\n");
  557.  
  558.     EndOutput();
  559. }
  560.  
  561. /* ShowSubtopics:
  562.  *  Print a list of subtopic names
  563.  */
  564. #define PER_LINE 4
  565.  
  566. static void
  567. ShowSubtopics(key, subtopics)
  568.     KEY *key;                /* the topic */
  569.     boolean *subtopics;        /* (out) are there any subtopics */
  570. {
  571.     int subt = 0;            /* printed any subtopics yet? */
  572.     KEY *subkey;            /* subtopic key */
  573.     int len;                /* length of key name */
  574.     char line[BUFSIZ];        /* subtopic output line */
  575.     char *start;            /* position of subname in key name */
  576.     int sublen;            /* length of subname */
  577.     int pos;
  578.     char *prev = NULL;        /* the last thing we put on the list */
  579.  
  580.     *line = '\0';
  581.     len = strlen(key->key);
  582.  
  583.     for (subkey = key+1; subkey->key != NULL; subkey++) {
  584.        if (strncmp(subkey->key, key->key, len) == 0) {
  585.           /* find this subtopic name */
  586.           start = subkey->key + len;
  587.           if (len > 0)
  588.             if (*start == ' ')
  589.              start++;        /* skip space */
  590.             else
  591.              break;        /* not the same topic after all  */
  592.           else            /* here we are looking for main topics */
  593.             if (!subkey->primary)
  594.              continue;    /* not a main topic */
  595.           sublen = instring(start, ' ');
  596.           if (prev == NULL || strncmp(start, prev, sublen) != 0) {
  597.              if (subt == 0) {
  598.                 subt++;
  599.                 if (len)
  600.                   (void) sprintf(line, "\nSubtopics available for %s:\n", 
  601.                         key->key);
  602.                 else
  603.                   (void) sprintf(line, "\nHelp topics available:\n");
  604.                 OutLine(line);
  605.                 *line = '\0';
  606.                 pos = 0;
  607.              }
  608.              if (pos == PER_LINE) {
  609.                 (void) strcat(line, "\n");
  610.                 OutLine(line);
  611.                 *line = '\0';
  612.                 pos = 0;
  613.              }
  614.              /* adapted by DvdSchaaf */
  615.              {
  616. #define FIRSTCOL    6
  617. #define COLLENGTH    15
  618.                 int spacelen, ispacelen;
  619.  
  620.                  if( pos == 0 )
  621.                     spacelen = FIRSTCOL;
  622.                  for( ispacelen = 0;
  623.                     ispacelen < spacelen; ispacelen++ )
  624.                     (void) strcat(line, " ");
  625.                  /* commented out *
  626.                  (void) strcat(line, "\t");
  627.                  */
  628.                  (void) strncat(line, start, sublen);
  629.                  spacelen = COLLENGTH - sublen;
  630.                  if( spacelen <= 0 )
  631.                     spacelen = 1;
  632.              }
  633.              pos++;
  634.              prev = start;
  635.           }
  636.        } else {
  637.           /* new topic */
  638.           break;
  639.        }
  640.     }
  641.     
  642.     /* put out the last line */
  643.     if (subt > 0 && pos > 0) {
  644.        (void) strcat(line, "\n");
  645.        OutLine(line);
  646.     }
  647.     
  648. /*
  649.     if (subt == 0) {
  650.        OutLine("\n");
  651.        OutLine("No subtopics available\n");
  652.     }
  653. */
  654.     
  655.     if (subtopics)
  656.      *subtopics = (subt != 0);
  657. }
  658.  
  659.  
  660. /* StartOutput:
  661.  * Open a file pointer to a pipe to user's $PAGER, if there is one,
  662.  * otherwise use our own pager.
  663.  */
  664. static void
  665. StartOutput()
  666. {
  667. #if defined(unix) || defined(AMIGA_SC_6_1)
  668. #if defined(unix)
  669.     extern FILE *popen();
  670. #endif
  671.     char *pager_name = getenv("PAGER");
  672.  
  673.     if (pager_name != NULL && *pager_name != '\0')
  674.      if ((outfile = popen(pager_name, "w")) != (FILE *)NULL)
  675.        return;            /* success */
  676.     outfile = stderr;
  677.     /* fall through to built-in pager */
  678. #endif
  679.  
  680.     /* built-in pager */
  681.     pagelines = 0;
  682. }
  683.  
  684. #if defined(ATARI) || defined(MTOS)
  685. #ifndef READLINE
  686. #error cannot compile atari versions without -DREADLINE
  687. #endif
  688. char tos_getch();    /* from readline.c */
  689. #endif
  690.  
  691. /* write a line of help output  */
  692. /* line should contain only one \n, at the end */
  693. static void
  694. OutLine(line)
  695.     char *line;
  696. {
  697.     int c;                /* dummy input char */
  698. #if defined(unix) || defined(AMIGA_SC_6_1)
  699.     if (outfile != stderr) {
  700.        fputs(line, outfile);
  701.        return;
  702.     }
  703. #endif
  704.  
  705.     /* built-in dumb pager */
  706.     /* leave room for prompt line */
  707.     if (pagelines >= SCREENSIZE - 2) {
  708.        fprintf(stderr,"Press return for more: ");
  709. #if !defined(ATARI) && !defined(MTOS)
  710.        do 
  711.         c = getchar();
  712.        while (c != EOF && c != '\n');
  713. #else
  714.        do 
  715.         c = tos_getch();
  716.        while (c != '\x04' && c != '\r' && c != '\n');
  717. #endif
  718.        pagelines = 0;
  719.     }
  720.     fputs(line, stderr);
  721.     pagelines++;
  722. }
  723.  
  724. static void
  725. EndOutput()
  726. {
  727. #if defined(unix) || defined(AMIGA_SC_6_1)
  728. #if defined(unix)
  729.     extern int pclose();
  730. #endif
  731.  
  732.     if (outfile != stderr)
  733.      (void) pclose(outfile);
  734. #endif
  735. }
  736.